/**
 * Calculadora.java
 * @author Oscar Castañeda 11086 Sección 20
 * @since 16/07/2012
 * @version 2
 * Descripción: Calculadora capaz de evaluar una expresión Post Fix ingresada
 *              sin errores (letras, espacios bien colocados). Utiliza una
 *              pila para su funcionamiento.
 */

public class Calculadora {

    private Stack<Double> pila;     //Pila usada por la calculadora para almacenar operandos numéricos.
    private String[] operadores;    //Lista que contiene a los operandos numéricos y operadores en el orden introducido.
    private double primerOperando;  //Primer número a operar.
    private double segundoOperando; //Segundo número a operar.
    private boolean syntaxError;    //Bandera que se coloca en true si no hay al menos dos elementos en pila al operar.
    private boolean mathError;      //Bandera que se coloca en true si hay un error aritmético en una operación
                                    //(Para este caso, exclusivamente división por cero)
    
    /**
     * Crea una nueva instancia de la calculadora, la cual cuenta con su propia
     * pila y no tiene ninguna bandera de error activada.
     */
    public Calculadora(){
        pila = new StackVector<Double>();
        syntaxError = false;
        mathError = false;
    }
    
    /**
     * Reinicia la calculadora limpiando sus registros de error y vaciando
     * su pila.
     */
    private void reiniciarCalculadora(){
        syntaxError = false;
        mathError = false;
        while (!(pila.empty())){         //Mientras la pila no este vacía
            pila.pop();                  //se extrae la cima de esta.
        }
    }
    
    /**
     * Hace que la computadora recorra la lista operadores, siempre y cuando no
     * se haya detectado un error en el proceso, para determinar si se encuentra
     * frente a una operación o un número y realizar la acción correspondiente.
     */
    private void identificarOperar(){
        for (int i=0; i < operadores.length; i++){
            if ( (!(operadores[i].equals(""))) && !(mathError||syntaxError) ){
                //Si el operador no es un espacio en blanco y no hay error alguno.
                if ( (operadores[i].equals("+")) || (operadores[i].equals("-")) || (operadores[i].equals("*")) || (operadores[i].equals("/"))){
                    //Si es una operación
                    if (pila.size() >= 2){
                        //Y hay al menos dos operandos en pila, se puede realizar la operación.
                        segundoOperando = pila.pop();
                        primerOperando = pila.pop();
                        if (operadores[i].equals("+"))
                            primerOperando = primerOperando + segundoOperando;
                        else{
                            if (operadores[i].equals("-"))
                                primerOperando = primerOperando - segundoOperando;    
                            else{
                                if (operadores[i].equals("*"))
                                    primerOperando = primerOperando * segundoOperando;
                                else{
                                    primerOperando = primerOperando / segundoOperando;
                                    if (segundoOperando == 0)
                                        mathError = true; //División dentro de cero levanta bandera.
                                        //No se maneja con excepción, puesto que en double, división
                                        //dentro de cero resulta en infinito.
                                }
                            }
                        }
                        pila.push(primerOperando); //Se almacena el resultado.
                    }else{
                        syntaxError = true; //Ya que no habían suficientes operandos.
                    }                    
                }else{
                    //De no ser una de las operaciones, debería de ser un número.
                    //Se comprobará que lo sea.
                    try{
                        pila.push(new Double (operadores[i]));
                    }catch (NumberFormatException noNumero){
                        syntaxError = true;
                        //Si no es un número, se levanta la bandera de error
                        //de sintaxis.
                    }
                }                    
            }
        }
    }
    
    /**
     * Devuelve el resultado de última computación realizada, que corresponde a
     * la cima de la pila como una cadena. Si existió un errror, se devuelve
     * en la cadena un indicador de dicho error.
     * @return Cadena con el resultado de la evaluación de la expresión PostFix.
     */
    private String getResult(){
        if (mathError)
            return "Math ERROR";
        else{
            if (syntaxError)
                return "Syntax ERROR";
            else{
                Double resultado = pila.pop();
                return resultado.toString();
            }
        }
    }
    
    /**
     * Hace que la calculadora evalúe la expresión Post Fix ingresada y devuelve
     * el resultado en un String.
     * @param expPostFix Expresión sin errores en formato Post Fix.
     * @return String informando el resultado de la operación.
     */
    public String computar(String expPostFix){
        reiniciarCalculadora();
        operadores = expPostFix.split(" ");
        identificarOperar();
        return getResult();
    }
    
}